<?php
namespace Bundle\ModuleBundle\DataSource;

use Doctrine\ORM;

use Doctrine\DBAL\Schema;

use Doctrine\DBAL\Schema\Comparator;

class DefaultDataSource extends DataSource
{
	/**
	 * Doctrine EntityManager
	 * @var EntityManager
	 */
	private $_entityManager;
	
	private $_query;
	
	private $_repository;
	
    protected function preboot()
    {
        $this->_entityClassName = $this->_moduleManager->getEntityClassNameTemplate($this->_module->getName());
        
        $this->_entityManager = $this->_moduleManager->getEntityManager();
    }

    public function all()
    {
    	
    	
    	if($this->_query != null)
    	{
    		throw new \Exception('Logic error');
    	}
    	
    	$qb = new ORM\QueryBuilder($this->_entityManager);
    	
    	$this->_query = $qb->select('d')->from($this->getEntityClassName(), 'd')->getQuery();
    }
    
    public function boot()
    {
    	// поиск отличий в структуре базы и сущности
    	$shemaManager = $this->_entityManager->getConnection()->getSchemaManager();
    	
    	// если класс не существует, подгружаем
        if(!\class_exists($this->getEntityClassName()))
        {
            eval($this->constructEntityClass());
        }
        
        $metadata = $this->_entityManager->getClassMetadata($this->getEntityClassName());
    	
    	// информация о структуре таблицы в базе
    	try 
    	{
            $tableDetails = $shemaManager->listTableDetails($this->_configuration->database['table']);
    	   
            $entityTableDetails = clone $tableDetails;
            
            foreach($metadata->fieldMappings as $field => $mapping)
            {
            	if(!$entityTableDetails->hasColumn($mapping['columnName']))
            	{
            		// @TODO Добавить параметр `options = array`
            		$entityTableDetails->addColumn($mapping['columnName'], $mapping['type']);
            		
            		$this->_moduleManager->getLogger()->info(sprintf('Found new column (%s@%s) in entity, adding to table definition.', 
            		  $mapping['columnName'], $mapping['type']));
            	}
            }
            
            $comparator = new Schema\Comparator();
            
            $tableDiff = $comparator->diffTable($tableDetails, $entityTableDetails);
            
            if($tableDiff != false)
            {
                $shemaManager->alterTable($tableDiff);
                $this->_moduleManager->getLogger()->info(sprintf('Table (%s) was updated to last entity structure.', 
                    $this->_configuration->database['table']));            	
            }
    	}
    	catch (\Exception $e)
    	{
    		// SQLSTATE[42S02]: Base table or view not found...
    		if($e->getCode() == '42S02')
    		{
    			//@TODO Это скорее всего не самый безопасный код для создания структуры таблицы.
    			$this->_moduleManager->getLogger()->info(sprintf('Table (%s) not found. Creating.', 
    			     $this->_configuration->database['table']));
                $tool = new \Doctrine\ORM\Tools\SchemaTool($this->_entityManager);
                
                
                $tool->createSchema(array($metadata));    			     
    			
                $this->_moduleManager->getLogger()->info(sprintf('Table (%s) created.', 
                     $this->_configuration->database['table']));
    		} else 
    		die(\var_dump($e));
    	}
    	
    	$this->_repository = $this->_entityManager->getRepository($this->_entityClassName);
    }
    /**
     * @todo Возможно, следует переделать
     */
    public function fetchData()
    {
    	if($this->_query != null)
    	return new DataSet($this->_query->execute());
    }
    
    public function find($id)
    {
    	$this->_query = $this->_entityManager->createQueryBuilder()->add('select', 'u')
                    ->add('from', $this->_entityClassName . ' u')
                    ->add('where', 'u.id = ?1')
                    ->setParameter(1, $id)
                    ->getQuery();
    }
    
    public function persist($datarow)
    {
    	$this->_entityManager->persist($datarow->getItem());
    	
    	$this->_entityManager->flush();
    }
}
 
